Skip to content

fix(contexts): map each node of an imported application to its own table or view - #3200

Open
rubenvdlinde wants to merge 1 commit into
feat/noid/app-menu-export-importfrom
fix/noid/import-context-node-mapping
Open

rubenvdlinde wants to merge 1 commit into
feat/noid/app-menu-export-importfrom
fix/noid/import-context-node-mapping

Conversation

@rubenvdlinde

@rubenvdlinde rubenvdlinde commented Nov 2, 2026 •

Copy link
Copy Markdown

Part of #3099, on top of #3138 (menu export and import). When a user's data is moved to another instance (TablesMigrator), an application now comes along once, with every table and view in it pointing at the table or view it was imported as.

Size: 364 lines: 133 code and 231 tests.

What was wrong

TablesMigrator::import() called ContextService::importContext() for every application once per imported table, before the views were imported. importContext() then pointed every node of the application at that one table (setNodeId($table->getId())), whatever the node was. An application with two tables and a view therefore came out as two applications, each with three nodes on the same table, the view node included. Measured with that application on an earlier version of this stack, which had the same import:

imports: 2 relations: [[0,5151],[0,5151],[1,5151],[0,5152],[0,5152],[1,5152]]

What changes

  • The migrator imports the tables and views first, keeps the old→new ids of both (importViews() now returns the view ids), and then imports each application once.
  • importContext(array $context, string $ownerId, array $tableIds, array $viewIds) maps every node to its own imported table or view. A node whose table or view didn't come along is left out, and so are the page contents and widget sources that showed it. An application none of whose nodes came along is not imported, as before.
  • Widget sources follow their node for every mapped node. Before, a source was only kept when it showed the table being imported, because every other node pointed at the wrong table.
  • Menu items that open a table or a view keep it. The import finds them by the uuid in the export, the way the scheme import does, among the nodes of the application. The migrator imports tables and views with the uuid they had, so this lifts the skipping of view items as well. An item whose table or view didn't come along is skipped with the reason in the log.

How it was tested

On Nextcloud 34 with PostgreSQL:

  • ContextPagesRoundTripTest: a database round trip of an application with two tables, a view and a widget on each. Every node, start page content and widget source lands on its own table or view.
  • MenuServiceTest: a menu with an item on a table and an item on a view is exported and imported with new node ids. Both items open the new relation of their node, and an item on a view that didn't come along is skipped. With views left unmapped, this test, the round trip above and the ContextServiceTest one below fail.
  • ContextServiceTest: nodes, page contents and widget sources of tables, a view and a node that didn't come along, the owner, and an application without imported nodes.
  • TablesMigratorTest: an application with two tables and a view is imported once, with the ids of both tables and the view.
  • the full unit suite on PostgreSQL: 617 tests
  • composer cs:check, psalm and rector, all clean
  • playwright/e2e/context.spec.ts on PostgreSQL with Nextcloud 34: 7 passed. Its first test, "Update and add resources", also passed on SQLite with Nextcloud 35

The user migration itself (occ user:export / user:import) was not run: the user_migration app is not on the test instance.

🖼️ Screenshots

No UI changes.

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔙 Backport requests are created or not needed: /backport to stableX.X
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

@rubenvdlinde
rubenvdlinde force-pushed the fix/noid/import-context-node-mapping branch from 6d3a321 to 4603c99 Compare November 3, 2026 14:51
…ble or view

Moving a user's data imported an application once for every table it
held, and pointed every node of each copy at that one table, views
included. The migrator now imports the tables and views first and then
each application once, mapping every node to the table or view it was
imported as. Page contents and widget sources follow the node they
showed, and menu items that open a table or a view keep theirs; a node
that was not imported is left out.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Ruben van der Linde <ruben@conduction.nl>
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-export-import branch from 2471ff4 to 6368c26 Compare November 4, 2026 12:35
@rubenvdlinde
rubenvdlinde force-pushed the fix/noid/import-context-node-mapping branch from 4603c99 to 88e3e69 Compare November 4, 2026 12:35
@rubenvdlinde
rubenvdlinde marked this pull request as ready for review November 6, 2026 08:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant